package com.lap.mybatis.starter.processor;

import com.lap.mybatis.starter.annotation.MyBatch;
import com.lap.mybatis.starter.exception.ParameterException;
import com.lap.mybatis.starter.exception.ProxyTargetException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Objects;

/**
 *
 *
 * <pre>
 * 批量处理代理。
 * 使用spring 增强功能,对Mapper 进行代理。
 * </pre>
 *
 * @author Shuisheng.Lao
 * @version 0.0.1
 */
public record MybatisProcessor(MybatisHelper mybatisHelper) implements BeanPostProcessor {

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    Mapper mapper = AnnotationUtils.findAnnotation(bean.getClass(), Mapper.class);
    if (Objects.nonNull(mapper)) {
      String namespace = getNamespace(bean);
      return executeProxy(bean, namespace);
    }
    return bean;
  }

  // @formatter on
  @SuppressWarnings("unchecked")
  private Object executeProxy(Object object, String namespace) {
    Class<?> clazz = object.getClass();
    return Proxy.newProxyInstance(
        clazz.getClassLoader(),
        clazz.getInterfaces(),
        (proxy, method, args) -> {
          MyBatch myBatch = AnnotationUtils.findAnnotation(method, MyBatch.class);
          if (Objects.isNull(myBatch)) return method.invoke(object, args);

          validateMethodParameters(method);

          String statementId = namespace + "." + method.getName();
          if (isList(method.getParameterTypes()[0])) {
            List<Object> dataList = (List<Object>) args[0];
            return mybatisHelper.executeBatch(statementId, dataList, myBatch.commitCount());
          }
          return method.invoke(object, args);
        });
  }

  // @formatter off

  private void validateMethodParameters(java.lang.reflect.Method method) throws ParameterException {
    if (method.getParameterCount() != 1) {
      throw new ParameterException("The method parameter must be equal to 1");
    }
  }

  private boolean isList(Class<?> clazz) {
    return clazz.getTypeName().equalsIgnoreCase("java.util.List")
        || clazz.getTypeName().equalsIgnoreCase("java.util.Set");
  }

  private String getNamespace(Object proxy) {
    Class<?> targetClass = AopUtils.getTargetClass(proxy);
    Class<?>[] targetInterfaces = targetClass.getInterfaces();
    if (ArrayUtils.isEmpty(targetInterfaces))
      throw new ProxyTargetException("Mapper interface is null or target class length lt 0.");

    return targetInterfaces[0].getName();
  }
}
